package _5_multithread._1_basic;

/**
 * <pre>
 * 代码运行                 锁             T1.state           T2.state
 * T1:synchronized(lock)
 * ...                    T1持有          RUNNABLE           BLOCKED
 * T1: sleep(1000)
 * ...                    T1持有          TIMED_WAITING      BLOCKED
 * lock.wait()            T1不持有, T2持有
 *                                        WAITING           RUNNABLE
 * ...
 * T2: sleep(2000)        T2持有           WAITING           TIMED_WAITING
 * ...
 * T2： lock.notify()   仍然是T2持有       WAITING           RUNNABLE
 * notify()不释放锁
 * ...
 * T2 执行完           T2不再持有, T1持有    RUNNABLE          TERMINATED
 * ...
 * T1 执行完                               TERMINATED         TERMINATED
 * </pre>
 */
public class _2_Wait_Notify {

  final static Object lock = new Object();
  private final static long start = System.currentTimeMillis();

  // wait 会释放目标对象的锁， Thread.sleep 不释放锁, 让出cpu, 调用 Thread.sleep方法的线程 sleep

  // notify（） 不会释放任何锁, 当前线程仍然持有锁, 仅仅是唤醒 *锁对象* 的等待队列, 让队列中的线程按策略去竞争锁
  // 等到当前线程放弃锁(执行完synchronized代码块或其他方法放弃锁), *锁对象* 等待队列的线程才可能获得锁

  // 所以： wait() 方法可以用 notify 方法唤醒
  public static void main(String[] args) throws InterruptedException {
    Thread T1 = new Thread(new T1());
    Thread T2 = new Thread(new T2());
    System.out.println("未执行 start方法, T1状态: " + T1.getState() + ", T2状态: " + T2.getState());
    T1.start();
    Thread.sleep(200); // 保证 T1先获得锁
    T2.start();
    for (int i = 0; i < 6; i++) {
      Thread.sleep(1000);
      System.out.println((System.currentTimeMillis() - start) + "毫秒: T1 状态: " + T1.getState());
      System.out.println((System.currentTimeMillis() - start) + "毫秒: T2 状态: " + T2.getState());
      System.out.println();
    }
  }


  public static class T1 implements Runnable {

    @Override
    public void run() {
      System.out.println("调用start之后, 进入run方法的执行, T1状态: " + Thread.currentThread().getState());
      synchronized (lock) {
        System.out.println((System.currentTimeMillis() - start) + "毫秒: T1线程: 获得 *锁对象*");
        try {
          Thread.sleep(1300); // 不释放锁, T1状态: TIMED_WAITING, T2状态: BLOCKED
          lock.notify();
          // *锁对象* 执行 wait()方法, 把 当前线程(T1) 放到 *锁对象* 的 wait set, T1线程不再持有锁
          // T1线程会 等待(wait), 直到 *锁对象* 执行 notify/notifyAll方法, T1线程才可能被唤醒
          lock.wait(); // wait 会释放锁, 释放后 T1状态: WAITING   T2状态: RUNNABLE
          System.out.println("T1 等待结束");
          long now = System.currentTimeMillis();
          while (System.currentTimeMillis() - now < 2000) {
          }
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println((System.currentTimeMillis() - start) + "毫秒: T1 end");
      }
    }
  }

  public static class T2 implements Runnable {

    @Override
    public void run() {
      synchronized (lock) {
        System.out.println((System.currentTimeMillis() - start) + "毫秒: T2 获得锁");
        long now = System.currentTimeMillis();
        while (System.currentTimeMillis() - now < 1000) {
        }
        // notify 在此处，因 T1 中，wait(5000) ，但，没到5s，就notify，所以 T1 在 T2 sleep 2s后，会唤醒，向下执行
        // 如果不加 object.notify() 就要等 5s 超时后，T1才能唤醒并向下执行
        // System.out.println((System.currentTimeMillis() - start) / 1000 + " s: T2 notify object");
        try {
          // sleep 不会释放锁，所以在sleep 2秒期间， T1 也不会获得锁
          System.out.println((System.currentTimeMillis() - start) + "毫秒: T2 执行 sleep(2000)");
          Thread.sleep(2000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println((System.currentTimeMillis() - start) + "毫秒: T2 中执行 lock.notify()");
        lock.notify(); // 调用了 notify 也会把当前线程的调用栈执行完， 等到当前线程执行完成 synchronized 代码块, 会释放锁
        now = System.currentTimeMillis();
        while (System.currentTimeMillis() - now < 1000) {
        }
        System.out.println((System.currentTimeMillis() - start) + "毫秒: T2 end");
      }
    }
  }

}
